package tp.pr3.cityLoader;


import java.io.IOException;
import java.io.InputStream;
import java.util.StringTokenizer;
import tp.pr3.Direction;
import tp.pr3.Street;
import tp.pr3.City;
import tp.pr3.Place;
import tp.pr3.cityLoader.cityLoaderExceptions.*;
import tp.pr3.items.*;

public class CityLoaderFromTxtFile 

{
    private	Place[] places = null;
	private Street[] streets =  null;
	private Place initialPlace=null;	
	
	private String[] stringPlaces=null;
	private String[] stringStreets=null;
	private String[] stringItems=null;
	private String[] cityLines =null;
	private int[] indicators=null;


	
	
	public CityLoaderFromTxtFile()	{
		
	indicators = new int[8];
	for(int i=1;i<indicators.length;i++)
		{
		indicators[i]=-1;
		}
					
	}
	
	public City loadCity(java.io.InputStream file) throws java.io.IOException
	{
		String cityString = null;
	
		try
		  {
			cityString = inputToString(file);
		    cityLines = cityString.split("\n");
		    this.fileIndicators();
		    this.testIndicators();	
		    this.fillPlaces();
		    initialPlace=places[0];
		    this.fillStreets();
		    this.setItems();		      
		    
		}catch(WrongCityFormatException e)
		{
			throw new WrongCityFormatException("error en mapa");
		}
		
		return  new City(streets); 
	}
	
	public Place getInitialPlace()
	{
		return initialPlace;
	}
     	
	private static String inputToString(InputStream file)
			throws WrongCityFormatException
			{
		      try{   
		      
				byte[] b = new byte[file.available()];		
				file.read(b);
				String result = new String(b); 
				return result;
		        }catch(IOException e)
		        {
		        	throw new WrongCityFormatException();
		        }
			}
	
	private void fileIndicators() 
		{
		
		 for (int i = 0; i < cityLines.length; i++)
		  {
			switch(cityLines[i].toUpperCase())
			{
			 case "BEGINCITY":indicators[0]=i;			 
				 break;
			
			 case "BEGINPLACES":indicators[1]=i;
				 break;
			 case "ENDPLACES":indicators[2]=i;
			 	 break;
			 case "BEGINSTREETS":indicators[3]=i;
				 break;
			 case "ENDSTREETS":indicators[4]=i;
				 break;
			 case "BEGINITEMS":indicators[5]=i;
				 break;
			 case "ENDITEMS":indicators[6]=i;
			 	break;
			 case "ENDCITY": indicators[7]=i;
				 break;
			  			
		  }
		 
		}
	 }		 
		 
	private void testIndicators() throws WrongCityFormatException	
		{
		 for(int i=0;i<indicators.length;i++)
		 	{
			 if(indicators[i]==-1) throw new WrongCityFormatException();
		 	}
		 
		 for(int i=1;i<indicators.length;i++)
		 {
			if(indicators[i]<indicators[i-1])  throw new WrongCityFormatException();
		 }
		 
		}
	
	private void fillPlaces() throws WrongCityFormatException
		{
		try
		{			
		 int j=0;		 
		 stringPlaces= new String[indicators[2]-indicators[1]-1];
		 for (int i=indicators[1]+1;i<indicators[2];i++)
		 	{
			 stringPlaces[j]=cityLines[i];
			 j++;
		 	}
		 
		 places = new Place[indicators[2]-indicators[1]-1];
		 for(int k=0;k<stringPlaces.length;k++)
		 	{
			 places[k]=parsePlace(stringPlaces[k],k);
		 	}
		 
		 } catch (WrongCityFormatException e) {
	        	throw new WrongCityFormatException();
	        }
		
		}

	private Place parsePlace(String line,int expectedNumber) throws WrongCityFormatException
	{
		
		String name = null;
		Boolean exit = null;
		String description=null;
		String spaceDescription= new String();
		
		StringTokenizer tkn = new StringTokenizer(line);	
			
	
		if(!tkn.nextToken().equalsIgnoreCase("PLACE"))
			throw new WrongCityFormatException();
		
		if(!tkn.nextToken().equalsIgnoreCase(String.valueOf(expectedNumber)))
			throw new WrongCityFormatException();
		
	    name=tkn.nextToken();	
	    
	    int numeroTokens=tkn.countTokens();
	    if(tkn.countTokens()>2)
	     {
	    	for(int i=0;i<numeroTokens-1;i++)
	    		{
	    		 spaceDescription=spaceDescription.concat(tkn.nextToken() +" ");
	    		}   		
	    	description = spaceDescription;  
	     }
	    
	    else description = parseDescription(tkn.nextToken());  
	    
	    exit = !tkn.nextToken().equalsIgnoreCase("noSpaceShip");
	    return new Place(name,exit,description);
	}
	
	private String parseDescription(String rawDescription)
		{
		 rawDescription=rawDescription.replace('_', ' ');			
		return rawDescription;		
		}
	
	private void fillStreets() throws WrongCityFormatException
	{
		int j=0;		
		try{
			
		 stringStreets= new String[indicators[4]-indicators[3]-1];
		 for (int i=indicators[3]+1;i<indicators[4];i++)
		 	{
			 stringStreets[j]=cityLines[i];	
			 j++;
		 	}
		
		 streets = new Street[indicators[4]-indicators[3]-1];
		 for(int k=0;k<stringStreets.length;k++)
		 	{
			 streets[k]=parseStreet(stringStreets[k],k);
		 	}
		 
		 } catch (WrongCityFormatException e) {
	        	throw new WrongCityFormatException();
	        }		 
	}

	private Street parseStreet(String line,int expectedNumber) throws WrongCityFormatException
	{
		int source,target;
		boolean open=false;
		Direction direction=null;;
		String code=null;
		
	
		StringTokenizer tkn = new StringTokenizer(line);
	try 
	{
		if(!tkn.nextToken().equalsIgnoreCase("street"))
			throw new WrongCityFormatException();
		
		if(!tkn.nextToken().equalsIgnoreCase(String.valueOf(expectedNumber)))
			throw new WrongCityFormatException();
		
		if(!tkn.nextToken().equalsIgnoreCase("place"))
			throw new WrongCityFormatException();
		
		source=Integer.valueOf(tkn.nextToken());
		
		direction=Direction.valueOf(tkn.nextToken().toUpperCase());
		
		if(!tkn.nextToken().equalsIgnoreCase("place"))
			throw new WrongCityFormatException();
		
		target=Integer.valueOf(tkn.nextToken());
		
		open = tkn.nextToken().equalsIgnoreCase("open");
		if(!open)
			code=tkn.nextToken();
		
		return new Street(places[source],direction,places[target],open,code);
	
	}catch (Exception e) {
    	throw new WrongCityFormatException();
    }	
  }
	
	private void setItems() throws WrongCityFormatException
	{
	 int j=0;
	  try
	  {
		 stringItems= new String[indicators[6]-indicators[5]-1];
		 for (int i=indicators[5]+1;i<indicators[6];i++)
		 	{
			 stringItems[j]=cityLines[i];
			 j++;
		 	}
		 
		 for(int k=0;k<stringItems.length;k++)
		 	{
			 setItemFromLine(stringItems[k],k);
		 	}		
	   }catch (WrongCityFormatException e) {
       	throw new WrongCityFormatException();
       }		  
	}	
	
	
	private void setItemFromLine(String line, int expectedNumber)throws WrongCityFormatException
	 {
		String type = null; 
		String id = null;
		String description = null;
		String spaceDescription= new String();
		
	 try{
		StringTokenizer tkn = new StringTokenizer(line);
		type = tkn.nextToken();
		if(!validType(type)) throw new WrongCityFormatException();
		if(!tkn.nextToken().equalsIgnoreCase(String.valueOf(expectedNumber)))
			throw new WrongCityFormatException();
		id=tkn.nextToken();
		
		int numeroTokens=tkn.countTokens();
		
		if(type.equalsIgnoreCase("FUEL") && tkn.countTokens()>6)
				{
				for(int i=0;i<numeroTokens-4;i++)
					{
					spaceDescription=spaceDescription.concat(tkn.nextToken() +" ");
					}   		
				description = spaceDescription;  
				}
		else if(type.equalsIgnoreCase("GARBAGE") && tkn.countTokens()>3)
			{
				for(int i=0;i<numeroTokens-3;i++)
				{
					spaceDescription=spaceDescription.concat(tkn.nextToken() +" ");
				}				
				description = spaceDescription;  
			}
		else if(type.equalsIgnoreCase("CODECARD") && tkn.countTokens()>3)
			{
			for(int i=0;i<numeroTokens-3;i++)
				{
				spaceDescription=spaceDescription.concat(tkn.nextToken() +" ");
				}				
			   description = spaceDescription;  
			}
		
		else description = parseDescription(tkn.nextToken());
		
		switch(type.toUpperCase())
		 {
			case "GARBAGE":
				parseGarbage(id,description,tkn);
				break;
			case "FUEL":
				parseFuel(id,description,tkn);
				break;
			case "CODECARD":
				parseCodeCard(id,description,tkn);
				break;		
		 }
	 	} catch(WrongCityFormatException e)
	 	{
	 		throw new WrongCityFormatException();
	 	}
	 }
	
	private boolean validType(String Type)
	{
		switch(Type.toUpperCase())
		 {
		 case "GARBAGE": return true;
		 case "FUEL": return true;
		 case "CODECARD": return true;
		 default: return false;
		 }
	}
	
	private void parseGarbage(String id, String description, StringTokenizer tkn) throws WrongCityFormatException
		{
		Garbage item = null;
		int power=0;
		int placeid=0;
		
		try
		{
		power=Integer.valueOf(tkn.nextToken());
		
		if(!tkn.nextToken().equalsIgnoreCase("PLACE"))
			throw new WrongCityFormatException();
		
		placeid=Integer.valueOf(tkn.nextToken());
		item= new Garbage(id,description,power);
		insertItem(item,placeid);
		}catch(WrongCityFormatException e)
	 	 {
	 		throw new WrongCityFormatException();
	 	 }
		}
	
	private void parseFuel(String id, String description, StringTokenizer tkn) throws WrongCityFormatException
		{
		Fuel item = null;
		int power=0;
		int uses=0;
		int placeid=0;
		try
		{
			power=Integer.valueOf(tkn.nextToken());
			uses=power=Integer.valueOf(tkn.nextToken());
			
			if(!tkn.nextToken().equalsIgnoreCase("PLACE"))
				throw new WrongCityFormatException();
			
			placeid=Integer.valueOf(tkn.nextToken());
			item= new Fuel(id,description,power,uses);
			insertItem(item,placeid);
			
		}catch(WrongCityFormatException e)
	 	 {
	 		throw new WrongCityFormatException();
	 	 }
		
		}
	
	private void parseCodeCard(String id, String description, StringTokenizer tkn) throws WrongCityFormatException
		{
		 CodeCard item = null;
		 String code;
		 int placeid=0;
		 try
		 {
		  code=tkn.nextToken();
			if(!tkn.nextToken().equalsIgnoreCase("PLACE"))
				throw new WrongCityFormatException();
			
		  placeid=Integer.valueOf(tkn.nextToken());
		  item= new CodeCard(id,description,code);
		  insertItem(item,placeid);	
		 }catch(WrongCityFormatException e)
	 	 {
	 		throw new WrongCityFormatException();
	 	 }
		 
		}
	
	private void insertItem(Item item,int placeId)throws WrongCityFormatException
		{
		try
			{
		    places[placeId].addItem(item);
			}	catch(Exception e)
		 	{
		 		throw new WrongCityFormatException();
		 	}	
		}
	
}